package com.lifesting.java2as3.analysis;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Status;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.search.ui.ISearchQuery;
import org.eclipse.search.ui.ISearchResult;
import org.eclipse.search.ui.text.Match;

import com.lifesting.tool.j2as3.visit.AnalysisVisitor;

public class OuterClassesQuery implements ISearchQuery {

	private IJavaProject project;
	private OuterClassSearchResult result;
	private ASTParser parser;
	public OuterClassesQuery(IJavaProject project) {
		super();
		this.project = project;
		result = new OuterClassSearchResult(this);
		parser = ASTParser.newParser(AST.JLS3);
	}

	@Override
	public boolean canRerun() {
		return true;
	}

	@Override
	public boolean canRunInBackground() {
		return true;
	}

	@Override
	public String getLabel() {
		return "outer classes query for project " + project.getElementName();
	}

	@Override
	public ISearchResult getSearchResult() {
		return result;
	}

	@Override
	public IStatus run(IProgressMonitor monitor)
			throws OperationCanceledException {
		try {
			monitor.beginTask("Analizing outer classes", IProgressMonitor.UNKNOWN);
			AnalysisVisitor visitor = new AnalysisVisitor();
			IPackageFragment[] all_packages = collectAllPackages(this.project);
			String[] root_packages = reduceRootPackages(all_packages);
			visitor.setRootPackages(root_packages);
			List<ICompilationUnit> all_units = new ArrayList<ICompilationUnit>();
			for(IPackageFragment ipf : all_packages){
				ICompilationUnit[] units = ipf.getCompilationUnits();
				for (ICompilationUnit unit: units){
					all_units.add(unit);
				}
			}
			monitor.beginTask("Analizing outer classes", all_units.size());
			for (ICompilationUnit u : all_units){
				monitor.subTask("Seek outer references for "+u.getElementName());
				parser.setSource(u);
				parser.setResolveBindings(true);
				parser.createAST(monitor).accept(visitor);
				monitor.worked(1);
			}
			result.addMatches(visitor.getOuterElements());
			monitor.done();
			return Status.OK_STATUS;
		} catch (JavaModelException e) {
			throw new OperationCanceledException(e.getLocalizedMessage());
		}
	}

	private String[] reduceRootPackages(IPackageFragment[] all_packages) {
		List<String> pkg_filters = new ArrayList<String>();
		for (IPackageFragment pft: all_packages){
			String pkgName = pft.getElementName();
			boolean found_parent = false;
			for (Iterator<String> iter = pkg_filters.iterator(); iter.hasNext();){
				String iter_name = iter.next();
				if (iter_name.startsWith(pkgName)){ //a.b.c => a.b 
					iter.remove();
				}else if (pkgName.startsWith(iter_name)){
					if (!found_parent) found_parent = true;
				}
			}
			if (!found_parent)
				pkg_filters.add(pkgName);
		}
		return pkg_filters.toArray(new String[pkg_filters.size()]);	
	}

	private IPackageFragment[] collectAllPackages(IJavaProject project2)
			throws JavaModelException {
		List<IPackageFragment> fragments = new ArrayList<IPackageFragment>();
		IPackageFragmentRoot[] all_roots = project.getAllPackageFragmentRoots();
		for (IPackageFragmentRoot pfr : all_roots) {
			if (pfr.getKind() == IPackageFragmentRoot.K_SOURCE) {
				IJavaElement[] pfs = pfr.getChildren();
				for (IJavaElement je : pfs) {
					if (je instanceof IPackageFragment) {
						IPackageFragment a_pr = (IPackageFragment) je;
						if (a_pr.getChildren().length > 0)
							fragments.add(a_pr);
					}
				}
			}
		}
		return fragments.toArray(new IPackageFragment[fragments.size()]);
	}

}
